<?php

/**
 * Enter description here...
 *
 * $Id: ip_to_area_class.inc.php 2 2012-07-10 01:08:43Z 5330056@qq.com $
 */
class ip_to_area_class
{
	private $_db_fp = false;
	private $_index_begin_end = array();
	private $_index_count = 0;
	private $_arga = array();

	public function __construct($db_file, $is_load_memory = false)
	{
		if(file_exists($db_file))
		{
			if($is_load_memory)
			{
				$this->_db_fp = fopen('php://memory', 'wb+');
				fwrite($this->_db_fp, file_get_contents($db_file));
				rewind($this->_db_fp);
			}
			else
			{
				$this->_db_fp = fopen($db_file, 'rb');
			}
			$this->_index_begin_end = array($this->read_bin_data_uint(null, 4), $this->read_bin_data_uint(null, 4));
			$this->_index_count = ($this->_index_begin_end[1] - $this->_index_begin_end[0]) / 7 + 1;
			$this->_arga = array(0=>chr(0), 1=>chr(1), 2=>chr(2));
		}
		else
		{
			die('DBFile Not Exists or Open DBFile False');
		}
	}

	public function get_area_by_ip($ip)
	{
		if(!$this->_db_fp)
		{
			return false;
		}
		if(is_numeric($ip))
		{
			$ip_uint = $ip;
		}
		elseif(preg_match('/(?:\d{1,3}\.){3}\d{1,3}/', $ip))
		{
			$ip_uint =  sprintf('%u', ip2long($ip));
		}
		else
		{
			return false;
		}

		list($max, $min, $loop) = array($this->_index_count, 0, 0);
		while($loop < 32)
		{
			$loop ++;
			$midle = round(($max + $min) / 2, 0);
			$midle_offset = $midle * 7 + $this->_index_begin_end[0];
			$midle_uint_ip = $this->read_bin_data_uint($midle_offset, 4);
			
			if($ip_uint > $midle_uint_ip)
			{
				$end_uint_ip = $this->read_bin_data_uint(3, 4, 'cur');
				if(empty($end_uint_ip))
				{
					return false;
				}
				if($ip_uint < $end_uint_ip)
				{
					fseek($this->_db_fp, -7, SEEK_CUR);
					break;
				}

				$min = $midle;
				continue;
			}
			elseif($ip_uint < $midle_uint_ip)
			{
				$end_uint_ip = $this->read_bin_data_uint(3, 4, 'cur');
				if(empty($end_uint_ip))
				{
					return false;
				}
				if($ip_uint > $end_uint_ip)
				{
					fseek($this->_db_fp, -7, SEEK_CUR);
					break;
				}

				$max = $midle;
				continue;
			}
			else
			{
				break;
			}
		}
		if($loop == 32)
		{
			return false;
		}
		fseek($this->_db_fp, $this->read_bin_data_uint(null, 3) + 4);

		$address_1 = $address_2 = '';
		$arg = fread($this->_db_fp, 1);
		if($arg == $this->_arga[1])
		{
			fseek($this->_db_fp, $this->read_bin_data_uint(null, 3));
			$arg = fread($this->_db_fp, 1);
		}
		if($arg == $this->_arga[2])
		{
			$add_1_offset = $this->read_bin_data_uint(null, 3);
			$arg = fread($this->_db_fp, 1);
			$arg == $this->_arga[2] ? fseek($this->_db_fp, $this->read_bin_data_uint(null, 3)) : fseek($this->_db_fp, -1, SEEK_CUR);
			$address_2 = $this->read_char();
			fseek($this->_db_fp, $add_1_offset);
			$address_1 = $this->read_char();
		}
		else
		{
			fseek($this->_db_fp, -1, SEEK_CUR);
			$address_1 = $this->read_char();
			$arg = fread($this->_db_fp, 1);
			$arg == $this->_arga[2] ? fseek($this->_db_fp, $this->read_bin_data_uint(null, 3)) : fseek($this->_db_fp, -1, SEEK_CUR);
			$address_2 = $this->read_char();
		}
		return $address_1.' '.$address_2;
	}

	private function read_char()
	{
		$char_str = '';
		while(($char = fread($this->_db_fp, 1)) != $this->_arga[0])
		{
			$char_str .= $char;
		}
		return $char_str;
	}

	private function read_bin_data_uint($offset_int, $length, $offset_type = 'set')
	{
		$full = '';
		if(($mod = $length % 4) != 0)
		{
			$full = sprintf("%'".chr(0)."-".(4 - $mod)."s", $full);
		}
		$offset_type_arr = array('set'=>SEEK_SET, 'cur'=>SEEK_CUR, 'end'=>SEEK_END);
		$offset_int === null ? '' : fseek($this->_db_fp, $offset_int, $offset_type_arr[$offset_type]);
		$___t = fread($this->_db_fp, $length);
		if(empty($___t))
		{
			return false;
		}

		$t = unpack('L', $___t.$full);
		return sprintf('%u', $t[1]);
	}

	function __destruct()
	{
		$this->_db_fp ? fclose($this->_db_fp) : '';
	}
}
?>